package jinyilw.swing.rlaf.ui;

import jinyilw.swing.ComponentTools;
import jinyilw.swing.FontTools;
import jinyilw.swing.graphics.GraphicsTools;
import jinyilw.swing.rlaf.theme.RThemeColor;
import jinyilw.swing.rlaf.ui.icon.RArrowIcon;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.util.Enumeration;
import java.util.List;

/**
 * @author Jinyi
 */
public class RTableHeaderUI extends BasicTableHeaderUI
{
	private static final AlphaComposite alphaComposite = ComponentTools.alphaComposite;
	private TableCellRenderer originalHeaderRenderer;

	public static ComponentUI createUI(JComponent h)
	{
		return new RTableHeaderUI();
	}

	@Override
	public void installUI(JComponent c)
	{
		super.installUI(c);
		if (header != null)
		{
			originalHeaderRenderer = header.getDefaultRenderer();
			if ((originalHeaderRenderer != null)
					&& "sun.swing.table.DefaultTableCellHeaderRenderer".equals(
					originalHeaderRenderer.getClass().getName()))
			{
				header.setDefaultRenderer(new RDefaultHeaderRenderer());
			}
		}
		c.setFont(FontTools.defaultFont);
		c.setOpaque(false);
	}

	@Override
	public void uninstallUI(JComponent c)
	{
		if (header != null && (header
				.getDefaultRenderer() instanceof RDefaultHeaderRenderer))
		{
			header.setDefaultRenderer(originalHeaderRenderer);
		}
		super.uninstallUI(c);
	}

	protected Component getHeaderRendererComponent(int col)
	{
		if (header == null)
			return null;

		TableColumn tabCol = header.getColumnModel().getColumn(col);
		TableCellRenderer renderer = tabCol.getHeaderRenderer();
		if (renderer == null)
			renderer = header.getDefaultRenderer();
		return renderer.getTableCellRendererComponent(header.getTable(),
				tabCol.getHeaderValue(), false, false, -1, col);
	}

	private int getHeaderHeight()
	{
		if (header == null)
			return 0;

		int height = 0;
		boolean accomodatedDefault = false;
		TableColumnModel columnModel = header.getColumnModel();
		for (int column = 0; column < columnModel.getColumnCount(); column++)
		{
			TableColumn aColumn = columnModel.getColumn(column);
			boolean isDefault = (aColumn.getHeaderRenderer() == null);

			if (!isDefault || !accomodatedDefault)
			{
				Component comp = getHeaderRendererComponent(column);
				int rendererHeight = comp.getPreferredSize().height;
				height = Math.max(height, rendererHeight);
				if (isDefault && rendererHeight > 0)
				{
					Object headerValue = aColumn.getHeaderValue();
					if (headerValue != null)
					{
						headerValue = headerValue.toString();

						if (headerValue != null && !headerValue.equals(""))
							accomodatedDefault = true;
					}
				}
			}
		}
		return height + 4;
	}

	@Override
	public Dimension getPreferredSize(JComponent c)
	{
		if (header == null)
		{
			return new Dimension(0, 0);
		}
		long width = 0;
		Enumeration<TableColumn> enumeration = header.getColumnModel().getColumns();
		while (enumeration.hasMoreElements())
		{
			TableColumn aColumn = enumeration.nextElement();
			width = width + aColumn.getPreferredWidth();
		}
		if (width > Integer.MAX_VALUE)
		{
			width = Integer.MAX_VALUE;
		}
		return new Dimension((int) width, getHeaderHeight());
	}

	protected void updateRolloverColumn(int oldColumn, int newColumn)
	{
		if (header == null)
		{
			return;
		}
		header.repaint(header.getHeaderRect(oldColumn));
		header.repaint(header.getHeaderRect(newColumn));
	}

	@Override
	protected void rolloverColumnUpdated(int oldColumn, int newColumn)
	{
		// Empty to avoid multiple paints
	}

	@Override
	public void paint(Graphics g, JComponent c)
	{
		if ((header == null) || header.getColumnModel().getColumnCount() <= 0)
			return;

		boolean ltr = header.getComponentOrientation().isLeftToRight();
		Rectangle clip = g.getClipBounds();
		Point left = clip.getLocation();
		Point right = new Point(clip.x + clip.width - 1, clip.y);
		TableColumnModel cm = header.getColumnModel();
		int cMin = header.columnAtPoint(ltr ? left : right);
		int cMax = header.columnAtPoint(ltr ? right : left);

		if (cMin == -1)
			cMin = 0;

		if (cMax == -1)
			cMax = cm.getColumnCount() - 1;

		TableColumn draggedColumn = header.getDraggedColumn();
		Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
		int columnWidth;
		TableColumn aColumn;
		if (ltr)
		{
			for (int column = cMin; column <= cMax; column++)
			{
				aColumn = cm.getColumn(column);
				columnWidth = aColumn.getWidth();
				cellRect.width = columnWidth;
				if (aColumn != draggedColumn)
				{
					paintCell(g, cellRect, column);
				}
				cellRect.x += columnWidth;
			}
		} else
		{
			for (int column = cMax; column >= cMin; column--)
			{
				aColumn = cm.getColumn(column);
				columnWidth = aColumn.getWidth();
				cellRect.width = columnWidth;
				if (aColumn != draggedColumn)
				{
					paintCell(g, cellRect, column);
				}
				cellRect.x += columnWidth;
			}
		}

		// Paint the dragged column if we are dragging.
		if (draggedColumn != null)
		{
			int draggedColumnIndex = viewIndexForColumn(draggedColumn);
			Rectangle draggedCellRect = header
					.getHeaderRect(draggedColumnIndex);
			draggedCellRect.x += header.getDraggedDistance();
			paintCell(g, draggedCellRect, draggedColumnIndex);
		}
		rendererPane.removeAll();
	}

	protected void paintCell(Graphics g, Rectangle cellRect, int col)
	{
		if (header == null)
			return;
		Component component = getHeaderRendererComponent(col);
		rendererPane.paintComponent(g, component, null, cellRect.x, cellRect.y,
				cellRect.width, cellRect.height, true);
	}

	private int viewIndexForColumn(TableColumn aColumn)
	{
		if (header == null)
			return -1;

		TableColumnModel cm = header.getColumnModel();
		for (int column = 0; column < cm.getColumnCount(); column++)
		{
			if (cm.getColumn(column) == aColumn)
				return column;
		}
		return -1;
	}

	private class RDefaultHeaderRenderer extends DefaultTableCellRenderer
	{
		private static final long serialVersionUID = 7097556155660555638L;
		private JTable table = null;
		private int col = 0;
		private Font font;

		public RDefaultHeaderRenderer()
		{
			super();
			setHorizontalAlignment(JLabel.CENTER);
			setHorizontalTextPosition(SwingConstants.LEADING);
			setOpaque(false);
		}

		@Override
		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column)
		{
			this.table = table;
			this.col = column;
			setText(value != null ? value.toString() : "");
			setIcon(null);
			Font headerFont = header.getFont();
			if (font != headerFont && headerFont != null)
			{
				setFont(headerFont.deriveFont(Font.BOLD));
				font = headerFont;
			}
			if (table != null)
			{
				RowSorter<? extends TableModel> rowSorter = table.getRowSorter();
				List keyList = null;
				if (rowSorter != null)
					keyList = rowSorter.getSortKeys();
				if ((keyList != null) && (!keyList.isEmpty()))
				{
					SortKey sortKey = (SortKey) keyList.get(0);
					if (sortKey.getColumn() == table
							.convertColumnIndexToModel(col))
					{
						if (sortKey.getSortOrder().equals(SortOrder.ASCENDING))
						{
							setIcon(RArrowIcon.getNorthIcon());
						} else if (sortKey.getSortOrder()
								.equals(SortOrder.DESCENDING))
						{
							setIcon(RArrowIcon.getSouthIcon());
						}
					}
				}
			}
			return this;
		}

		@Override
		public void paint(Graphics g)
		{
			Graphics2D g2d = (Graphics2D) g;
			paintBackground(g2d);
			super.paint(g);
		}

		protected void paintBackground(Graphics2D g)
		{
			Composite composite = g.getComposite();
			int w = getWidth();
			int h = getHeight();
			int draggedColumn = -1;
			if (header.getTable() != null && header.getDraggedColumn() != null)
			{
				draggedColumn = header.getColumnModel().getColumnIndex(
						header.getDraggedColumn().getIdentifier());
			}

			g.setComposite(alphaComposite.derive(0.3f));
			if ((table != null) && table.isEnabled() && col == draggedColumn)
			{
				GraphicsTools.fillHorGradient(g,
						RThemeColor.getRolloverColors(), 0, 0, w, h);
			} else
			{
				GraphicsTools.fillHorGradient(g, RThemeColor.getDefaultColors(),
						0, 0, w, h);
			}
			g.setComposite(composite);
			g.setColor(RThemeColor.colorSet.borderColor);
			Stroke saveStroke = g.getStroke();
			g.setStroke(GraphicsTools.getLargeStroke());
			g.drawRect(0, 0, w, h);
			g.setStroke(saveStroke);
		}
	}
}
